

#import "GameNode.h"
#import "Bike.h"
#import "BikeContactListener.h"
#import "Box2D.h"
#import "Box2DHelper.h"
#define MOTOR_SPEED (10)

#define use_wheel

@interface Bike()
@end

@implementation Bike

@synthesize awake = _awake;
@synthesize diving = _diving;
#pragma mark -
+ (id) bikeWithGame:(GameNode*)game {
	return [[[self alloc] initWithGame:game] autorelease];
}

- (id) initWithGame:(GameNode*)game {
	
    //	if ((self = [super init])) {
    if ((self = [super initWithFile:@"bike.png"])) {
        
        
		self.game = game;
		
#ifndef DRAW_BOX2D_WORLD
        //        [self setTexture:[[CCTextureCache sharedTextureCache] addImage:@"resetButton.png"]];
        
#else
        self.visible=NO;
#endif
		body_ = NULL;
		_radius = 14.0f;
        
        //		_contactListener = new BikeContactListener(self);
        //		game_.world->SetContactListener(_contactListener);
        
		[self reset];
	}
	return self;
}

- (void) dealloc {
    
	self.game = nil;
	
#ifndef DRAW_BOX2D_WORLD
	self.sprite = nil;
#endif
    
	delete _contactListener;
	[super dealloc];
}

- (void) reset {
    [self destroyAllFixturesFromBody:body_];
	flying_ = NO;
	_diving = NO;
	_nPerfectSlides = 0;
	if (body_) {
		game_.world->DestroyBody(body_);
	}
#ifdef use_wheel
    if (wheel1Node_.body) {
        game_.world->DestroyBody(wheel1_);
        game_.world->DestroyBody(wheel2_);
        game_.world->DestroyBody(axle1_);
        game_.world->DestroyBody(axle2_);
        [game_.terrain removeChild:wheel1Node_ cleanup:YES];
        [game_.terrain removeChild:wheel2Node_ cleanup:YES];
        
        
    }
#endif
    [self createWheel];
	[self updateNode];
	[self sleep];
}
- (void) createWheel {
    //
    // Cart
    //
    // Reuses the already created body
    b2BodyDef bd;
	bd.type = b2_dynamicBody;
	bd.linearDamping = 0.05f;
	bd.fixedRotation = true;
	
	// start position
  CGPoint p = ccp(0, game_.screenH/2+_radius*3);
//    CGPoint p = ccp(0, 120);
	CCLOG(@"start position = %f, %f", p.x, p.y);
    
	bd.position.Set(p.x * [Box2DHelper metersPerPoint], p.y * [Box2DHelper metersPerPoint]);
	body_ = game_.world->CreateBody(&bd);
    
    b2Body *cart =body_;
    b2FixtureDef fd;
    b2PolygonShape polyShape;
    b2BodyDef bodyDef;
    
    // The cart is composed of 2 fixtures:
    // A box (lower part)
    // A 5-vertices poligon (upper part)
    
    // Create lower part of the cart
    polyShape.SetAsBox(1.0f, 0.25f);
    
    fd.friction		= 0.5f;
    fd.density		= 1;
    fd.restitution	= 0.0f;
    fd.filter.groupIndex = -kCollisionFilterGroupIndexBike;
    //    cart->CreateFixture(&fd);
    
    // Create upper part of the cart
    //*
    fd.shape = &polyShape;
    //row 1, col 1
    int num = 6;
    b2Vec2 verts[] = {
        b2Vec2(-30.6f / PTM_RATIO, -14.6f / PTM_RATIO),
        b2Vec2(30.4f / PTM_RATIO, -14.6f / PTM_RATIO),
        b2Vec2(30.6f / PTM_RATIO, -0.1f / PTM_RATIO),
        b2Vec2(20.0f / PTM_RATIO, 0.8f / PTM_RATIO),
        b2Vec2(15.9f / PTM_RATIO, 13.3f / PTM_RATIO),
        b2Vec2(-30.4f / PTM_RATIO, 14.1f / PTM_RATIO)
    };
       polyShape.Set(verts,num);
    /*/
     b2CircleShape cir;
     cir.m_radius=0.5;
     fd.shape =&cir;
     //*/
    cart->CreateFixture(&fd);
	
    cart->SetType(b2_dynamicBody);
#ifdef use_wheel
    //
    // Axles
    //
    // Axle 1
    bodyDef.position = cart->GetWorldCenter();
    bodyDef.type = b2_dynamicBody;
    axle1_ = game_.world->CreateBody(&bodyDef);
    //center ~ position
    polyShape.SetAsBox(0.05f, 0.05f, b2Vec2(-0.6f, -0.4f), 0 );
    fd.shape = &polyShape;
    fd.density	= 1;
    fd.filter.groupIndex =  -kCollisionFilterGroupIndexBike;
    axle1_->CreateFixture(&fd);
    // Axle 2
    axle2_ = game_.world->CreateBody(&bodyDef);
    polyShape.SetAsBox(0.05f, 0.05f, b2Vec2(0.7f, -0.4f), 0 );
    axle2_->CreateFixture(&fd);
    //
    // Suspensions
    //
    b2PrismaticJointDef prismaticDef;
    prismaticDef.Initialize(cart, axle1_, axle1_->GetWorldCenter() , b2Vec2(0, 1));
    prismaticDef.lowerTranslation = -0.1f;
    prismaticDef.upperTranslation = 0.0f;
    prismaticDef.enableLimit = true;
    spring1_ = (b2PrismaticJoint*)game_.world->CreateJoint(&prismaticDef);
    // Front Prismatic Joint
    prismaticDef.Initialize(cart, axle2_, axle2_->GetWorldCenter(), b2Vec2(0, 1));
    //HERE 负数表示向上的最大移动距离，正数向下，？？？？？？相对于谁呢
    prismaticDef.lowerTranslation = -0.1f;
    prismaticDef.upperTranslation = 0.0f;
    prismaticDef.enableLimit = true;
    spring2_ = (b2PrismaticJoint*)game_.world->CreateJoint(&prismaticDef);
    //
    // Wheels
    //
    bodyDef.position = axle1_->GetWorldCenter();
    wheel1_ = game_.world->CreateBody(&bodyDef);
    b2CircleShape	circleShape;
    circleShape.m_radius = 12/PTM_RATIO;
    fd.friction		= 0.5f;
    fd.density		= 10;
    fd.restitution	= 0.0f;
    fd.shape = &circleShape;
    fd.filter.groupIndex =  -kCollisionFilterGroupIndexBike;
    wheel1_->CreateFixture(&fd);
    
    // Front Wheel (Wheel 2)
    bodyDef.position = axle2_->GetWorldCenter();
    wheel2_ = game_.world->CreateBody(&bodyDef);
    wheel2_->CreateFixture(&fd);
    
    //
    // Motors
    //
    b2RevoluteJointDef revoluteDef;
    revoluteDef.Initialize(wheel1_, axle1_, wheel1_->GetWorldCenter() );
    motor1_ = (b2RevoluteJoint*) game_.world->CreateJoint(&revoluteDef);
    
    revoluteDef.Initialize(wheel2_, axle2_, wheel2_->GetWorldCenter() );
    motor2_ = (b2RevoluteJoint*) game_.world->CreateJoint(&revoluteDef);
    // calculate base spring force from the mass of the cart//
//    baseSpringForce_ = cart->GetMass() * 5;
    // Wheel 1
    wheel1Node_ = [[WheelNode alloc] initWithBody:wheel1_ game:game_];
    [game_.terrain addChild:wheel1Node_];
    [wheel1Node_ release];
    
    // Wheel 2
    wheel2Node_ = [[WheelNode alloc] initWithBody:wheel2_ game:game_];
    [game_.terrain addChild:wheel2Node_];
    [wheel2Node_ release];
    
    // roll the wheel
    /*
     motor1_->EnableMotor(true);
     motor1_->SetMotorSpeed((float32) (MOTOR_SPEED* M_PI));
     motor1_->SetMaxMotorTorque( 10 );
     
     motor2_->EnableMotor(true);
     motor2_->SetMotorSpeed((float32) (MOTOR_SPEED* M_PI ));
     motor2_->SetMaxMotorTorque( 10 );
     //*/
#endif
}


- (void) sleep {
    [self wake:NO];
}
- (void) wake:(BOOL) wake{
    _awake = wake;
	body_->SetActive(wake);
#ifdef use_wheel
        wheel1_->SetActive(wake);
        wheel2_->SetActive(wake);
        axle1_->SetActive(wake);
        axle2_->SetActive(wake);
    
#endif
    
}
- (void) wake {
    [self wake:YES];
    //给一个右上角的冲量，使之向前跳跃
    //	body_->ApplyLinearImpulse(b2Vec2(1,2), body_->GetPosition());
}
-(void)rotateBackWheel:(BOOL)rotate{
#ifdef use_wheel
    if (rotate) {
        //        motor1_->EnableMotor(true);
        //        motor1_->SetMotorSpeed((float32) (MOTOR_SPEED* M_PI * 0.5));
        //        motor1_->SetMaxMotorTorque( 10 );
        
        motor2_->EnableMotor(true);
        motor2_->SetMotorSpeed((float32) (MOTOR_SPEED* M_PI * 0.5));
        motor2_->SetMaxMotorTorque( 10 );
    } else {
        //        motor1_->EnableMotor(false);
		motor2_->EnableMotor(false);
    }
#endif
}
- (void) updatePhysics {
    //    NSLog(@"Bike ---updatePhysics---- ");
	// apply force if diving
	if (_diving) {
		if (!_awake) {
			[self wake];
			_diving = NO;
		} else {
            body_->ApplyForce(b2Vec2(0,-40),body_->GetPosition());
            [self rotateBackWheel:YES];
		}
	}else{
        [self rotateBackWheel:NO];
    }
	
	// limit velocity
    
	const float minVelocityX = 3;
	const float minVelocityY = -40;
	b2Vec2 vel = body_->GetLinearVelocity();
	if (vel.x < minVelocityX) {
		vel.x = minVelocityX;
	}
	if (vel.y < minVelocityY) {
		vel.y = minVelocityY;
	}
    //FIXME 如果没有这一句，body会旋转90°，不晓得为什么
    body_->SetLinearVelocity(vel);
}
//change ccNode position and rotation angle
- (void) updateNode {
    //	NSLog(@"Bike ---updateNode---- ");
	CGPoint p;
	p.x = body_->GetPosition().x * [Box2DHelper pointsPerMeter];
	p.y = body_->GetPosition().y * [Box2DHelper pointsPerMeter];
	
	// CCNode position and rotation
	self.position = p;
	b2Vec2 vel = body_->GetLinearVelocity();
	float angle = atan2f(vel.y, vel.x);
    
#ifdef DRAW_BOX2D_WORLD
	body_->SetTransform(body_->GetPosition(), angle);
#else
	self.rotation = -1 * CC_RADIANS_TO_DEGREES(angle);
#endif
	
	// collision detection
	b2Contact *c = game_.world->GetContactList();
	if (c) {
		if (flying_) {
			[self landed];
		}
	} else {
		if (!flying_) {
			[self tookOff];
		}
	}
	
	// TEMP: sleep if below the screen
	if (p.y < -_radius && _awake) {
		[self sleep];
	}
#ifdef use_wheel
    //车体晃动
    /*
     spring1_->SetMaxMotorForce( baseSpringForce_ + (40 * baseSpringForce_ * powf(spring1_->GetJointTranslation(), 2) ) );
     spring1_->SetMotorSpeed( -20 * spring1_->GetJointTranslation() );
     
     spring2_->SetMaxMotorForce( baseSpringForce_ + (40 * baseSpringForce_ * powf(spring2_->GetJointTranslation(), 2) ) );
     spring2_->SetMotorSpeed( -20 * spring2_->GetJointTranslation() );
     */
    
    //旋转车轮
    /*
     motor1_->EnableMotor(true);
     motor1_->SetMotorSpeed((float32) (MOTOR_SPEED* M_PI * 0.5));
     motor1_->SetMaxMotorTorque( 10 );
     
     motor2_->EnableMotor(true);
     motor2_->SetMotorSpeed((float32) (MOTOR_SPEED* M_PI * 0.5));
     motor2_->SetMaxMotorTorque( 10 );
     */
#ifndef DRAW_BOX2D_WORLD
    [wheel1Node_ update:0];
    [wheel2Node_ update:0];
#endif
#endif
}

- (void) landed {
    //	CCLOG(@"landed");
	flying_ = NO;
}

- (void) tookOff {
    //	CCLOG(@"tookOff");
	flying_ = YES;
	b2Vec2 vel = body_->GetLinearVelocity();
    //	CCLOG(@"vel.y = %f",vel.y);
	if (vel.y > kPerfectTakeOffVelocityY) {
        //		CCLOG(@"perfect slide");
		_nPerfectSlides++;
		if (_nPerfectSlides > 1) {
			if (_nPerfectSlides == 4) {
				[game_ showFrenzy];
			} else {
				[game_ showPerfectSlide];
			}
		}
	}
}

- (void) hit {
    //	CCLOG(@"hit");
	_nPerfectSlides = 0;
	[game_ showHit];
}

- (void) setDiving:(BOOL)diving {
	if (_diving != diving) {
		_diving = diving;
		// TODO: change sprite image here
	}
}

@end

#pragma mark -
#pragma mark WheelNode

@implementation WheelNode
-(id) initWithBody:(b2Body*)body game:(GameNode*)game{
    if (self=[super initWithBody:body game:game image:@"wheel.png"]) {
        //        [self scheduleUpdate];
    }
#ifdef DRAW_BOX2D_WORLD
    self.visible=NO;
#endif
    return self;
}
-(void) update:(ccTime)dt
{
    //    NSLog(@"WheelNode --update---");
    b2Vec2 bPos = body_->GetPosition();
    //    (p.x * [Box2DHelper metersPerPoint], p.y * [Box2DHelper metersPerPoint])
    self.position = ccp( bPos.x * [Box2DHelper pointsPerMeter], bPos.y * [Box2DHelper pointsPerMeter] );
    self.rotation = -1 * CC_RADIANS_TO_DEGREES(body_->GetAngle());
    
}
- (void) hit {
    //	CCLOG(@"hit");
	_nPerfectSlides = 0;
	[game_ showHit];
}
-(void) preSolveContact:(b2Contact*)contact  manifold:(const b2Manifold*) oldManifold
{
	b2WorldManifold wm;
	contact->GetWorldManifold(&wm);
	b2PointState state1[2], state2[2];
	b2GetPointStates(state1, state2, oldManifold, contact->GetManifold());
	if (state2[0] == b2_addState) {
		const b2Body *b = contact->GetFixtureB()->GetBody();
		b2Vec2 vel = b->GetLinearVelocity();
		float va = atan2f(vel.y, vel.x);
		float na = atan2f(wm.normal.y, wm.normal.x);
        //		CCLOG(@"na = %.3f",na);
		if (na - va > kMaxAngleDiff) {
            [self hit];
		}
	}
    
}

@end
